/*
 * JBoss, Home of Professional Open Source
 * Copyright 2011, Red Hat, Inc. and individual contributors
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

/*
 * 15/07/13 - Change notice:
 * This file has been modified by Mobius Software Ltd.
 * For more information please visit http://www.mobius.ua
 */
package ua.mobius.media.server.mgcp.tx.cmd;

import java.io.IOException;
import ua.mobius.media.server.mgcp.MgcpEvent;
import ua.mobius.media.server.mgcp.controller.MgcpCall;
import ua.mobius.media.server.mgcp.controller.MgcpConnection;
import ua.mobius.media.server.mgcp.controller.MgcpEndpoint;
import ua.mobius.media.server.mgcp.controller.naming.UnknownEndpointException;
import ua.mobius.media.server.mgcp.message.MgcpRequest;
import ua.mobius.media.server.mgcp.message.MgcpResponse;
import ua.mobius.media.server.mgcp.message.MgcpResponseCode;
import ua.mobius.media.server.mgcp.message.Parameter;
import ua.mobius.media.server.mgcp.tx.Action;
import ua.mobius.media.server.scheduler.Scheduler;
import ua.mobius.media.server.scheduler.Task;
import ua.mobius.media.server.scheduler.TaskChain;
import ua.mobius.media.server.utils.Text;
import org.apache.log4j.Logger;
/**
 * Modify connection command.
 * 
 * @author Oifa Yulian
 */
public class DeleteConnectionCmd extends Action {
    //response strings
    private final static Text CALLID_MISSING = new Text("Missing call identifier");
    private final static Text UNKNOWN_CALL_IDENTIFIER = new Text("Could not find this call with specified identifier");
    private final static Text CONNECTIONID_EXPECTED = new Text("Connection identifier was not specified");
    private final static Text SUCCESS= new Text("Success");
    
    private MgcpRequest request;
    
    private Parameter connectionID;
    
    //local and domain name parts of the endpoint identifier
    private Text localName = new Text();
    private Text domainName = new Text();
            
    //layout local and domain names into endpoint identifier
    private Text[] endpointName = new Text[]{localName, domainName};      
    
    private MgcpEndpoint endpoint;
    private MgcpEndpoint[] endpoints = new MgcpEndpoint[1];

    private TaskChain handler;

    private Scheduler scheduler;
    
    //error code and message
    private int code;
    private Text message;
    
    private int tx,rx;
    
    private final static Logger logger = Logger.getLogger(DeleteConnectionCmd.class);    
    
    public DeleteConnectionCmd(Scheduler scheduler) {
    	this.scheduler=scheduler;
        handler = new TaskChain(2,scheduler);
        
        Delete delete = new Delete();
        Responder responder = new Responder();
        
        handler.add(delete);
        handler.add(responder);
        
        ErrorHandler errorHandler = new ErrorHandler();
        
        this.setActionHandler(handler);
        this.setRollbackHandler(errorHandler);        
    }
    
    private class Delete extends Task {
        
        public Delete() {
            super();
        }

        public int getQueueNumber()
        {
        	return scheduler.MANAGEMENT_QUEUE;
        }

        private void deleteForEndpoint(MgcpRequest request) {
            //getting endpoint name
            request.getEndpoint().divide('@', endpointName);
            //searching endpoint
            try {
                int n = transaction().find(localName, endpoints);
                if (n == 0) {
                    throw new MgcpCommandException(MgcpResponseCode.ENDPOINT_NOT_AVAILABLE, new Text("Endpoint not available"));
                }
            } catch (UnknownEndpointException e) {
                    throw new MgcpCommandException(MgcpResponseCode.ENDPOINT_UNKNOWN, new Text("Endpoint not available"));
            }
            
            //extract found endpoint
            endpoint = endpoints[0];            
            endpoint.deleteAllConnections();            
        }

        private void deleteForCall(Parameter callID, MgcpRequest request) {
            //getting call
            MgcpCall call = transaction().getCall(callID.getValue().hexToInteger(), false);
            if (call == null) {
                throw new MgcpCommandException(MgcpResponseCode.INCORRECT_CALL_ID, UNKNOWN_CALL_IDENTIFIER);
            }
            
            call.deleteConnections();
        }
        
        @Override
        public long perform() {
            request = (MgcpRequest) getEvent().getMessage();
            
            Parameter callID = request.getParameter(Parameter.CALL_ID);
            connectionID = request.getParameter(Parameter.CONNECTION_ID);
            
            if (callID == null && connectionID == null) {
                this.deleteForEndpoint(request);
                return 0;
            }
            
            if (callID != null && connectionID == null) {
                this.deleteForCall(callID, request);
                return 0;
            }
            
            if (callID == null) {
                throw new MgcpCommandException(MgcpResponseCode.PROTOCOL_ERROR, CALLID_MISSING);
            }
            
            //getting call
            MgcpCall call = transaction().getCall(callID.getValue().hexToInteger(), false);
            if (call == null) {
                throw new MgcpCommandException(MgcpResponseCode.INCORRECT_CALL_ID, UNKNOWN_CALL_IDENTIFIER);
            }
            
            if (connectionID == null) {
                throw new MgcpCommandException(MgcpResponseCode.PROTOCOL_ERROR, CONNECTIONID_EXPECTED);
            }
            
            //getting endpoint name
            request.getEndpoint().divide('@', endpointName);
            //searching endpoint
            try {
                int n = transaction().find(localName, endpoints);
                if (n == 0) {                	
                    throw new MgcpCommandException(MgcpResponseCode.ENDPOINT_NOT_AVAILABLE, new Text("Endpoint not available"));
                }
            } catch (UnknownEndpointException e) {
                    throw new MgcpCommandException(MgcpResponseCode.ENDPOINT_UNKNOWN, new Text("Endpoint not available"));
            }
            //extract found endpoint
            endpoint = endpoints[0];
                       
            MgcpConnection connection = endpoint.getConnection(connectionID.getValue().hexToInteger());
            
            if(connection!=null)
            {
            	rx = connection.getPacketsReceived();
            	tx = connection.getPacketsTransmitted();
            
            	endpoint.deleteConnection(connectionID.getValue().hexToInteger());
            }
            
            return 0;
        }
        
    }
    
    /**
     * Searches endpoint specified in message.
     * 
     * The result will be stored into variable endpoint.
     */
    private class EndpointLocator extends Task {

        public EndpointLocator() {
            super();
        }
        
        public int getQueueNumber()
        {
        	return scheduler.MANAGEMENT_QUEUE;
        }

        @Override
        public long perform() {
            try {
                //searching endpoint
                int n = transaction().find(localName, endpoints);
                
                if (n == 0) {
                    throw new MgcpCommandException(MgcpResponseCode.ENDPOINT_NOT_AVAILABLE, new Text("Endpoint not available"));
                }

                //extract found endpoint
                endpoint = endpoints[0];
                
                //checking endpoint's state
                if (endpoint.getState() == MgcpEndpoint.STATE_BUSY) {
                    throw new MgcpCommandException(MgcpResponseCode.ENDPOINT_NOT_AVAILABLE, new Text("Endpoint not available"));
                }
            } catch (Exception e) {
                throw new MgcpCommandException(MgcpResponseCode.ENDPOINT_NOT_AVAILABLE, new Text("Endpoint not available"));
            }
            
            return 0;
        }
        
    }
    
    
    private class Responder extends Task {

        public Responder() {
            super();
        }
        
        public int getQueueNumber()
        {
        	return scheduler.MANAGEMENT_QUEUE;
        }

        @Override
        public long perform() {
            MgcpEvent evt = transaction().getProvider().createEvent(MgcpEvent.RESPONSE, getEvent().getAddress());
            MgcpResponse response = (MgcpResponse) evt.getMessage();
            response.setResponseCode(MgcpResponseCode.TRANSACTION_WAS_EXECUTED);
            response.setResponseString(SUCCESS);
            response.setTxID(transaction().getId());

            if (connectionID != null) {
                response.setParameter(Parameter.CONNECTION_ID, connectionID.getValue());
            }
            response.setParameter(Parameter.CONNECTION_PARAMETERS, new Text("PS=" + tx + ", PR=" + rx));

            try {
                transaction().getProvider().send(evt);
            } catch (IOException e) {
            	logger.error(e);
            } finally {
                evt.recycle();
            }
            
            return 0;
        }
        
    }

    private class ErrorHandler extends Task {

        public ErrorHandler() {
            super();
        }
        
        public int getQueueNumber()
        {
        	return scheduler.MANAGEMENT_QUEUE;
        }

        @Override
        public long perform() {
            code = ((MgcpCommandException)transaction().getLastError()).getCode();
            message = ((MgcpCommandException)transaction().getLastError()).getErrorMessage();
            
            MgcpEvent evt = transaction().getProvider().createEvent(MgcpEvent.RESPONSE, getEvent().getAddress());
            MgcpResponse response = (MgcpResponse) evt.getMessage();
            response.setResponseCode(code);
            response.setResponseString(message);
            response.setTxID(transaction().getId());

            if (connectionID != null) {
                response.setParameter(Parameter.CONNECTION_ID, connectionID.getValue());
            }

            try {
                transaction().getProvider().send(evt);
            } catch (IOException e) {
            	logger.error(e);
            } finally {
                evt.recycle();
            } 
            
            return 0;
        }
        
    }
    
}
